package database

import expr.DB
import expr.Query
import query.select.NativeSelect
import query.select.Select
import query.select.ValuesSelect
import query.select.WithSelect
import visitor.checkOLAP
import visitor.getExpr
import java.sql.Connection
import javax.sql.DataSource

class DBConnection(source: DataSource, override val db: DB) : DataBaseImpl() {
    override val isTransaction: Boolean = false

    private var dataSource: DataSource = source

    inline fun transaction(isolation: Int? = null, query: DBTransaction.() -> Unit) {
        checkOLAP(this.db)

        val conn = getConnection()
        conn.autoCommit = false
        isolation?.let { conn.transactionIsolation = it }

        try {
            query(DBTransaction(this.db, conn))
            conn.commit()
        } catch (e: Exception) {
            e.printStackTrace()
            conn.rollback()
        } finally {
            conn.autoCommit = true
            conn.close()
        }
    }

    override fun getConnection(): Connection {
        return this.dataSource.connection
    }

    override fun select(vararg columns: String): Select {
        val select = Select(db, getConnection(), isTransaction, this)
        select.select(*columns)
        return select
    }

    override infix fun select(query: Query): Select {
        val select = Select(db, getConnection(), isTransaction, this)
        select.invoke(query)
        return select
    }

    override infix fun select(query: List<Query>): Select {
        val select = Select(db, getConnection(), isTransaction, this)
        select.invoke(query)
        return select
    }

    override fun select(vararg query: Query): Select {
        val select = Select(db, getConnection(), isTransaction, this)
        select.select(*query)
        return select
    }

    override fun select(): Select {
        return Select(db, getConnection(), isTransaction, this)
    }

    override fun nativeSelect(sql: String, vararg arg: Any): NativeSelect {
        val argList = arg.map { getExpr(it).toString() }
        var nativeSql = sql
        if (sql.contains("?")) {
            argList.forEach {
                nativeSql = nativeSql.replaceFirst("?", it)
            }
        }
        return NativeSelect(db, nativeSql, getConnection(), isTransaction, this)
    }

    override fun with(): WithSelect {
        return WithSelect(db, getConnection(), isTransaction, this)
    }

    override fun values(vararg value: List<Any>) : ValuesSelect {
        return ValuesSelect(db, getConnection(), isTransaction, this)
    }
}